#pragma once
#include<iostream>
#include<queue>
#include<pthread.h>

template<class T>
class ThreadPool
{
	private:
		std::queue<T> q;
		pthread_mutex_t lock;
		pthread_cond_t cond;
		static ThreadPool<T>* instance;
	private:
		ThreadPool()
		{
			pthread_mutex_init(&lock,nullptr);
			pthread_cond_init(&cond,nullptr);
		}
		ThreadPool(const ThreadPool<T>&) = delete;
	public:
		static ThreadPool<T>* get_instance(int num)
		{
			static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
			if(instance == nullptr)
			{
				pthread_mutex_t lock(&mtx);
				if(instance == nullptr)
				{
					instance = new ThreadPool<T>();
					instance->InitThreadPool(num);
				}
				pthread_mutex_t unlock(&mtx);
			}
			return instance;
		}
		void LockQueue()
		{
			pthread_mutex_lock(&lock);
		}
		void UnlockQueue()
		{
			pthread_mutex_unlock(&lock);
		}
		bool IsEmpty()
		{
			return q.size() == 0;
		}
		void ThreadWait()
		{
			pthread_cond_wait(&cond,&lock);
		}
		void ThreadWakeup()
		{
			pthread_cond_signal(&cond);
		}
		void PopTask(T* out)
		{
			*out = q.front();
			q.pop();
		}
		void PushTask(const T& in)
		{
			LockQueue();
			q.push(in);
			ThreadWakeup();
			UnlockQueue();
		}
		static void* Routine(void* args)
		{
			pthread_detach(pthread_self());
			ThreadPool* tp = (ThreadPool*)args;
			while(1)
			{
				tp->LockQueue();
				while(tp->IsEmpty())
				{
					tp->ThreadWait();
				}
				T t;
				tp->PopTask(&t);
				tp->UnlockQueue();
				t();
			}
			return nullptr;
		}
		void InitThreadPool(int num)
		{
			for(auto i = 0; i < num; ++i)
			{
				pthread_t tid;
				pthread_create(&tid,nullptr,Routine,this);
			}
		}
		~ThreadPool()
		{
			pthread_mutex_destroy(&lock);
			pthread_cond_destroy(&cond);
		}
};
template<class T>
ThreadPool<T>* ThreadPool<T>::instance = nullptr;

